package com.cjm.one.security.fillter;



import cn.hutool.jwt.JWT;
import com.cjm.one.common.constant.RedisConstants;
import com.cjm.one.common.constant.TokenConstants;

import com.cjm.one.common.result.Result;
import com.cjm.one.common.result.ResultCodeEnum;
import com.cjm.one.common.utils.JSONUtils;
import com.cjm.one.common.utils.ResponseUtil;
import com.cjm.one.model.system.entity.SysUserInfo;
import com.cjm.one.model.system.vo.LoginVo;
import com.cjm.one.security.custom.CustomUser;
import com.cjm.one.security.excetion.LoginException;
import com.cjm.one.system.client.SyStemUserClient;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 登录过滤器，继承UsernamePasswordAuthenticationFilter，对用户名密码进行登录校验
 * </p>
 *
 */
@Slf4j
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private RedisTemplate redisTemplate;

    private SyStemUserClient syStemUserClient;


    public TokenLoginFilter(AuthenticationProvider authenticationProvider, RedisTemplate redisTemplate, SyStemUserClient syStemUserClient) {
        this.setAuthenticationManager(new ProviderManager(authenticationProvider));
        this.setPostOnly(false);
        //指定登录接口及提交方式，可以指定任意路径
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/auth/system/login","POST"));
        this.redisTemplate = redisTemplate;
        this.syStemUserClient = syStemUserClient;
    }

    /**
     * 登录认证
     * @param req
     * @param res
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
            throws AuthenticationException {
        try {
            LoginVo loginVo = new ObjectMapper().readValue(req.getInputStream(), LoginVo.class);
            //获取验证码
            String code = loginVo.getCode();
            String codeKey = loginVo.getCodeKey();
            String codeValue = (String) redisTemplate.opsForValue().get(RedisConstants.CAPTCHA_IMG_KEY+codeKey);
            if (null == codeValue){
                throw new LoginException(ResultCodeEnum.CAPTCHA_TIMEOUT_ERROR);
            }
            if (!code.equalsIgnoreCase(codeValue)){
                throw new LoginException(ResultCodeEnum.VALIDATECODE_ERROR);
            }

            log.info("登录认证:{}",loginVo);
            Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
            return this.getAuthenticationManager().authenticate(authenticationToken);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }

    /**
     * 登录成功
     * @param request
     * @param response
     * @param chain
     * @param auth
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication auth) throws IOException, ServletException {
        CustomUser customUser = (CustomUser) auth.getPrincipal();
        log.info("登录成功customUser:{}",customUser);
        SysUserInfo sysUser = customUser.getSysUser();
        String token = TokenConstants.TOKEN_PREFIX + JWT.create()
                .setSigner(TokenConstants.SIGNER)
                .setPayload(TokenConstants.SYS_USER, sysUser)
                .sign();

        //将sysUser放入到redis
        redisTemplate.opsForValue().set(RedisConstants.SYS_USER_LOGIN_KEY_TOKEN + token,
                JSONUtils.toString(sysUser) ,
                TokenConstants.TOKEN_EXPIRATION_TIME,
                TimeUnit.SECONDS);

        //保存权限数据
        //redisTemplate.boundHashOps("admin:auth").put(customUser.getUsername(), customUser.getAuthorities());

        //记录日志
//         sysLoginLog = new SysLoginLog();
//        sysLoginLog.setUsername(customUser.getUsername());
//        sysLoginLog.setStatus(1);
//        sysLoginLog.setIpaddr(IpUtil.getIpAddress(request));
//        sysLoginLog.setMsg("登录成功");
//        sysLoginLogFeignClient.recordLoginLog(sysLoginLog);
//
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        ResponseUtil.out(response, Result.ok(map));
    }

    /**
     * 登录失败
     * @param request
     * @param response
     * @param e
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException e) throws IOException, ServletException {

        if(e.getCause() instanceof RuntimeException) {
            ResponseUtil.out(response, Result.build(null, 204, e.getMessage()));
        } else {
            log.error("登录失败:{}", e.getMessage());
            ResponseUtil.out(response, Result.build(null, ResultCodeEnum.ACCOUNT_ERROR.getCode(),e.getMessage()));
        }
    }
}
